#pragma once
#include <strmif.h>
#include <string>
#include "mkvmuxinpinvideo.hpp"
#include "mkvmuxinpinaudio.hpp"
#include "mkvmuxoutpin.hpp"
#include "mkvcontext.hpp"

namespace MkvMux
{

class Filter : public IBaseFilter,
               public IMediaSeeking
{
    friend HRESULT CreateInstance(
            IClassFactory*,
            IUnknown*, 
            const IID&, 
            void**);
    
    Filter(IClassFactory*, IUnknown*);
    virtual ~Filter();
    
    Filter(const Filter&);
    Filter& operator=(const Filter&);
    
public:

    //TODO: we should probably implement IMediaSeeking, even
    //some crippled implementation that allows you to SetPos(0)
    //and query for GetCurrentPos.

    //IUnknown

    HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    
    //IBaseFilter

    HRESULT STDMETHODCALLTYPE GetClassID(CLSID*);    
    HRESULT STDMETHODCALLTYPE Stop();    
    HRESULT STDMETHODCALLTYPE Pause();    
    HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME);    
    HRESULT STDMETHODCALLTYPE GetState(DWORD, FILTER_STATE*);    
    HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock*);
    HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock**);
    HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins**);    
    HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR, IPin**);    
    HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO*);    
    HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph*, LPCWSTR);    
    HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR*);

    ////IFileSourceFilter
    //
    //HRESULT STDMETHODCALLTYPE Load(LPCOLESTR, const AM_MEDIA_TYPE*);    
    //HRESULT STDMETHODCALLTYPE GetCurFile(LPOLESTR*, AM_MEDIA_TYPE*);
    
    //IMediaSeeking interface:

    HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD*);    
    HRESULT STDMETHODCALLTYPE CheckCapabilities(DWORD*);    
    HRESULT STDMETHODCALLTYPE IsFormatSupported(const GUID*);
    HRESULT STDMETHODCALLTYPE QueryPreferredFormat(GUID*);    
    HRESULT STDMETHODCALLTYPE GetTimeFormat(GUID*);    
    HRESULT STDMETHODCALLTYPE IsUsingTimeFormat(const GUID*);    
    HRESULT STDMETHODCALLTYPE SetTimeFormat(const GUID*);    
    HRESULT STDMETHODCALLTYPE GetDuration(LONGLONG*);    
    HRESULT STDMETHODCALLTYPE GetStopPosition(LONGLONG*);    
    HRESULT STDMETHODCALLTYPE GetCurrentPosition(LONGLONG*);    

    HRESULT STDMETHODCALLTYPE ConvertTimeFormat( 
        LONGLONG*,
        const GUID*,
        LONGLONG,
        const GUID*);
    
    HRESULT STDMETHODCALLTYPE SetPositions( 
        LONGLONG*,
        DWORD,
        LONGLONG*,
        DWORD);
    
    HRESULT STDMETHODCALLTYPE GetPositions( 
        LONGLONG*,
        LONGLONG*);
    
    HRESULT STDMETHODCALLTYPE GetAvailable( 
        LONGLONG*,
        LONGLONG*);
    
    HRESULT STDMETHODCALLTYPE SetRate(double);    
    HRESULT STDMETHODCALLTYPE GetRate(double*);    
    HRESULT STDMETHODCALLTYPE GetPreroll(LONGLONG*);

    //local classes and methods

    class Lock
    {
        Lock(const Lock&);
        Lock& operator=(const Lock&);
    public:
        Lock();
        ~Lock();

        HRESULT Seize(Filter*, DWORD timeout = 5000);
        void Release();
    private:
        HANDLE m_hMutex;
    };
    
private:

    class nondelegating_t : public IUnknown
    {
    public:
    
        Filter* const m_pFilter;
        LONG m_cRef;
        
        nondelegating_t(Filter*);
        virtual ~nondelegating_t();
        
        HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
        ULONG STDMETHODCALLTYPE AddRef();
        ULONG STDMETHODCALLTYPE Release();

    private:

        nondelegating_t(const nondelegating_t&);
        nondelegating_t& operator=(const nondelegating_t&);

    };
    
    IClassFactory* const m_pClassFactory;
    nondelegating_t m_nondelegating;
    IUnknown* const m_pOuter;  //decl must follow m_nondelegating
    REFERENCE_TIME m_start;
    IReferenceClock* m_clock;
    FILTER_INFO m_info;
    HANDLE m_hMutex;
    
public:

    FILTER_STATE m_state;

    InpinVideo m_inpin_video;
    InpinAudio m_inpin_audio;
    Outpin m_outpin;
    
    MkvContext m_mkv;
    
    HRESULT OnEndOfStream();

};

}  //end MkvMux
